<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>惊蛰的博客 面试-简述基础中剩下的部分BIO，NIO，粘包/拆包，多路复用等</title>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <style>
        @import url('/css/post-card.css');
        @import url('/css/main.css');
        @import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,400;0,600;1,400;1,600&family=Roboto+Slab:wght@700&display=swap');
    </style>
    <style>
        body {
            font-family: Montserrat, sans;
        }

        .page-footer {
            color: var(--text-p3);
        }

        .sitemap {
            display: flex;
            justify-content: space-between;
            margin: .5rem 0 2rem;
            display: grid;
            grid-gap: 1rem 1rem;
            grid-auto-flow: column dense;
        }

        .sitemap-group {
            display: flex;
            flex-direction: column;
            align-items: flex-start;
        }

        .sitemap-group>.fs14 {
            margin: .5rem 0;
        }

        .page-footer .sitemap .sitemap-group>a {
            margin: .2rem 0;
            color: var(--text-p3);
        }

        .page-footer .text p {
            margin: 4px 0;
            line-height: 1.2;
        }
    </style>
    <script src="https://lib.baomitu.com/jquery/3.6.0/jquery.min.js"></script>
    <script src="/js/index.js"></script>
    <link href="https://cdn.bootcdn.net/ajax/libs/jquery.scrollbar/0.2.9/jquery.scrollbar.min.css" rel="stylesheet">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery_lazyload/1.9.7/jquery.lazyload.min.js"></script>
<meta name="generator" content="Hexo 5.4.0"></head>

<body>
    
<div class="l_body">
    <div class="l_main list">
        
<div class="nav-wrap mobile-hidden">
    <nav class="sub post cap header1">
        <a  href="/">最近发布</a>
        <a 
            href="/wiki/">分类</a>
        <a 
            href="/notes/">标签</a>
        <a href="/bibi/">说说</a>

    </nav>
    <div class="post-body-title header2">
        面试-简述基础中剩下的部分BIO，NIO，粘包/拆包，多路复用等
    </div>
</div>

<style>
    .sub2 {
        display: flex;
        flex-direction: column;
        position: absolute;
        z-index: 11;
        background:#ffff!important;
        border-radius: 5px;
    }

    .sub2 a{
        display: block;
        margin-left: 0px!important;
        border-radius: 5px;
        margin: 5px 0 3px 0!important;
    }
</style>


<script>
    $(function () {
        //页面初始化的时候，获取滚动条的高度（上次高度）
        var start_height = $(document).scrollTop();
        //获取导航栏的高度(包含 padding 和 border)
        var navigation_height = $('.header1').outerHeight();
        console.log(start_height, navigation_height);
        $(window).scroll(function () {
            //触发滚动事件后，滚动条的高度（本次高度）
            var end_height = $(document).scrollTop();
            console.log(end_height);
            //触发后的高度 与 元素的高度对比
            if (end_height > navigation_height) {
                $('.header1').addClass('hide');
                $('.header2').addClass('hide');
            } else {
                $('.header1').removeClass('hide');
                $('.header2').removeClass('hide');
            }
            //触发后的高度 与 上次触发后的高度 比较
            if (end_height < start_height) {
                $('.header1').removeClass('hide');
                $('.header2').removeClass('hide');
            }
            //再次获取滚动条的高度，用于下次触发事件后的对比
            start_height = $(document).scrollTop();
        });
    });
</script>


        <h2>
            面试-简述基础中剩下的部分BIO，NIO，粘包/拆包，多路复用等
        </h2>

        <div class="body-main-content">
            <h5 id="简述基础中剩下的部分BIO，NIO，粘包-拆包，多路复用等"><a href="#简述基础中剩下的部分BIO，NIO，粘包-拆包，多路复用等" class="headerlink" title="简述基础中剩下的部分BIO，NIO，粘包/拆包，多路复用等"></a><strong>简述基础中剩下的部分BIO，NIO，粘包/拆包，多路复用等</strong></h5><span id="more"></span>
<p><img src="" data-original="https://i.loli.net/2020/12/02/1bCl5pJOPwQLKjY.png"></p>
<h2 id="BIO，NIO和AIO"><a href="#BIO，NIO和AIO" class="headerlink" title="BIO，NIO和AIO"></a><strong>BIO，NIO和AIO</strong></h2><p><strong>简介：</strong></p>
<blockquote>
<p>BIO：同步阻塞式IO，服务器实现模式为一个连接一个线程，即客户端有连接请求时服务器端就需要启动一个线程进行处理，如果这个连接不做任何事情会造成不必要的线程开销，当然可以通过线程池机制改善。 </p>
</blockquote>
<blockquote>
<p>NIO：同步非阻塞式IO，服务器实现模式为一个请求一个线程，即客户端发送的连接请求都会注册到多路复用器上，多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。 </p>
</blockquote>
<blockquote>
<p>AIO(NIO.2)：异步非阻塞式IO，服务器实现模式为一个有效请求一个线程，客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。</p>
</blockquote>
<h3 id="BIO"><a href="#BIO" class="headerlink" title="BIO"></a><strong>BIO</strong></h3><blockquote>
<p><img src="" data-original="https://i.loli.net/2020/12/02/ug8zDUsTCdBJMct.png"><br>阻塞IO，一个请求对应一个线程，线程是阻塞的，面向流的，只能单向读写</p>
</blockquote>
<p>缺点：很显而易见，阻塞式IO、弹性伸缩能力强、多线程耗费资源</p>
<p>场景：<strong>BIO方式适用于连接数目比较小且固定的场景，这种方式对服务器资源要求比较高</strong>，并发局限于应用中。</p>
<h3 id="NIO"><a href="#NIO" class="headerlink" title="NIO"></a><strong>NIO</strong></h3><blockquote>
<p><img src="" data-original="https://i.loli.net/2020/12/02/ZILvo8VTjXUSJlN.png"><br>同步非阻塞，服务器实现一个连接一个线程，即客户端发送的连接请求都会注册到多路复用器上，多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。NIO方式适用于连接数目多且连接比较短（轻操作）的架构，比如聊天服务器，并发局限于应用中，编程比较复杂，JDK1.4之后开始支持。每个客户端请求都会注册到Selector(多路复用器)，是面向缓冲的, 可以双向读写</p>
</blockquote>
<p>相对于BIO来说优点就是非阻塞节约资源了</p>
<p>缺点：NIO类库繁琐，复杂，可靠性不高，空轮询，会导致cpu100%</p>
<p>场景：<strong>NIO适合处理连接数目特别多，但是连接比较短（轻操作）的场景</strong>，<strong>Jetty，Mina，ZooKeeper等都是基于java nio实现</strong>。</p>
<p>NIO在JAVA中的代码（部分）</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br></pre></td><td class="code"><pre><span class="line">import java.io.IOException;</span><br><span class="line">import java.net.InetSocketAddress;</span><br><span class="line">import java.nio.ByteBuffer;</span><br><span class="line">import java.nio.channels.*;</span><br><span class="line">import java.util.Iterator;</span><br><span class="line"></span><br><span class="line">/**</span><br><span class="line"> * 阻塞点this.selector.select();</span><br><span class="line"> * 轮询器虽然是一个线程内部也是线程池</span><br><span class="line"> */</span><br><span class="line">public class NioSocket &#123;</span><br><span class="line">    private Selector selector;   //通道管理器(管理器)</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * 初始化Channel并绑定端口</span><br><span class="line">     * @param port</span><br><span class="line">     * @throws IOException</span><br><span class="line">     */</span><br><span class="line">    public void initServer(int port) throws IOException &#123;</span><br><span class="line">        ServerSocketChannel serverChannel = ServerSocketChannel.open();</span><br><span class="line">        serverChannel.configureBlocking(false);  //非阻塞</span><br><span class="line">        serverChannel.socket().bind(new InetSocketAddress(port));</span><br><span class="line"></span><br><span class="line">        this.selector = Selector.open();</span><br><span class="line">        serverChannel.register(selector, SelectionKey.OP_ACCEPT);</span><br><span class="line">        System.out.println(&quot;服务器已启动...&quot;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * 监听轮询器</span><br><span class="line">     * @throws IOException</span><br><span class="line">     */</span><br><span class="line">    public void listenSelector() throws IOException &#123;</span><br><span class="line">        //轮询监听selector</span><br><span class="line">        while (true)&#123;</span><br><span class="line">            //等待客户连接</span><br><span class="line">            //select模型,多路复用</span><br><span class="line">            //this.selector.select();   //在这里会阻塞,无论是连接还是客户端发送数据还是客户端关闭,这里都会触发</span><br><span class="line">            this.selector.selectNow();   //这里不阻塞会立即执行</span><br><span class="line">            Iterator&lt;SelectionKey&gt; iteKey = this.selector.selectedKeys().iterator();</span><br><span class="line">            while (iteKey.hasNext())&#123;</span><br><span class="line">                SelectionKey key = iteKey.next();</span><br><span class="line">                iteKey.remove();  //移除,防止重复处理</span><br><span class="line">                //处理请求</span><br><span class="line">                handler(key);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * 处理客户端请求</span><br><span class="line">     * @param key</span><br><span class="line">     */</span><br><span class="line">    private void handler(SelectionKey key) throws IOException &#123;</span><br><span class="line">        if (key.isAcceptable())&#123;  //处理连接请求</span><br><span class="line">            //处理客户端连接请求事件</span><br><span class="line">            ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();</span><br><span class="line">            SocketChannel socketChannel = serverChannel.accept();</span><br><span class="line">            //接受客户端发送的信息时,需要给通道设置读权限</span><br><span class="line">            socketChannel.configureBlocking(false);</span><br><span class="line">            socketChannel.register(selector,SelectionKey.OP_READ);</span><br><span class="line">        &#125;else if(key.isReadable())&#123;   //处理读请求</span><br><span class="line">            //处理读事件</span><br><span class="line">            SocketChannel socketChannel = (SocketChannel) key.channel();</span><br><span class="line">            ByteBuffer buffer = ByteBuffer.allocate(1024);</span><br><span class="line">            int readData = socketChannel.read(buffer);</span><br><span class="line">            if (readData&gt;0)&#123;</span><br><span class="line">                String info  = new String(buffer.array(),&quot;GBK&quot;).trim();</span><br><span class="line">                System.out.println(&quot;服务端收到数据: &quot;+Thread.currentThread()+info);</span><br><span class="line">            &#125;else &#123;</span><br><span class="line">                System.out.println(&quot;客户端关闭了...&quot;);</span><br><span class="line">                key.cancel();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public static void main(String[] args) throws IOException &#123;</span><br><span class="line">        NioSocket nio = new NioSocket();</span><br><span class="line">        nio.initServer(8888);</span><br><span class="line">        nio.listenSelector();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="AIO-NIO-2"><a href="#AIO-NIO-2" class="headerlink" title="AIO(NIO.2)"></a><strong>AIO(NIO.2)</strong></h3><blockquote>
<p> 异步非阻塞IO，在此种模式下，用户进程只需要发起一个IO操作然后立即返回，等IO操作真正的完成以后，应用程序会得到IO操作完成的通知，此时用户进程只需要对数据进行处理就好了，不需要进行实际的IO读写操作，因为真正的IO读取或者写入操作已经由内核完成了。<br><img src="" data-original="https://i.loli.net/2020/12/02/dFDNOUpPs28ofw5.png" alt="https://blog.csdn.net/weixin_43122090/article/details/105462088"></p>
</blockquote>
<h3 id="多路复用（Channel）"><a href="#多路复用（Channel）" class="headerlink" title="多路复用（Channel）"></a><strong>多路复用（Channel）</strong></h3><ul>
<li> Channel是一个对象，可以通过它读取和写入数据。通常我们都是将数据写入包含一个或者多个字节的缓冲区，然后再将缓存区的数据写入到通道中，将数据从通道读入缓冲区，再从缓冲区获取数据。</li>
<li> Channel 类似于原I/O中的流（Stream），但有所区别：</li>
<li> 流是单向的，通道是双向的，可读可写。</li>
<li> 流读写是阻塞的，通道可以异步读写。</li>
</ul>
<p>NIO是JDK1.4提供的操作，他的流还是流，没有改变，服务器实现的还是一个连接一个线程，当是：<code>客户端发送的连接请求都会注册到多路复用器上</code>，多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。NIO方式适用于连接数目多且连接比较短（轻操作）的架构，比如聊天服务器，并发局限于应用中，编程比较复杂，JDK1.4之后开始支持。</p>
<p><img src="" data-original="https://i.loli.net/2020/12/02/oCJDetfwlszS1bq.png"><br>上图清晰的描述了他们之间的关系</p>
<h3 id="拆包-粘包"><a href="#拆包-粘包" class="headerlink" title="拆包/粘包"></a><strong>拆包/粘包</strong></h3><h4 id="为什么会出现粘包"><a href="#为什么会出现粘包" class="headerlink" title="为什么会出现粘包"></a><strong>为什么会出现粘包</strong></h4><p>分两个方面解释，一方面是发送方原因<br>我们都知道，TCP默认用的是Nagle算法，只做两件事：<br>1）只有上一分组得到确认，才会发送下一分组<br>2）收集多个小分组，在一个确认到来时一起发送<br>所以，正是这个算法造成了粘包现象</p>
<p>另一方面就是接收方原因，<br>TCP接收到分组时，并不会立即发送至应用层进行处理，实际上，TCP会将分组放入缓存里，然后应用程序主动从缓存中读取，这样，如果TCP接受的速度远大于应用读取缓存的速度，多个包就会被存至缓存中，应用程序读的时候，就会读到好几个包首尾粘在一起</p>
<h4 id="什么时候需要处理粘包现象"><a href="#什么时候需要处理粘包现象" class="headerlink" title="什么时候需要处理粘包现象"></a><strong>什么时候需要处理粘包现象</strong></h4><p>1）如果发送方发送的是同一数据的不同部分，比如一个比较大的文件分成多个碎片传递过来，这时候不需要处理粘包<br>2）但是如果多个分组并不相干，甚至是并列关系，我们就一定要处理粘包问题了，不然就会丢弃分组或者分组错误的情况出现了</p>
<p><strong>如果处理粘包现象</strong><br>1）发送方我们可以通过关闭Nagle算法来解决，使用TCP_NODELAY来关闭该算法<br>2）接收方的，接收方没办法处理粘包现象，我们只能通过应用层面来解决改问题<br>3）应用层面，可以在发送分组的时候，将数据的长度一并发送，或者通过规定固定符号起始固定符号结束（该方法简单好用）</p>
<p>以上关于粘包内容摘抄整理自<a target="_blank" rel="noopener" href="https://blog.csdn.net/prefect_boy/article/details/100035865">https://blog.csdn.net/prefect_boy/article/details/100035865</a></p>
<hr>
<p><strong>能力越强，责任越大。</strong></p>
<p><strong>实事求是，严谨细致。</strong></p>

        </div>
        <div class="page-footer reveal fs12">
    <hr>
    <div class="sitemap">
        <div class="sitemap-group">
            <span class="fs14">博客</span>
            <a href="/">近期发布</a>
            <a href="/wiki/">分类</a>
            <a href="/notes/">标签</a>
            <a href="/archives/">归档</a>
        </div>
        <div class="sitemap-group">
            <span class="fs14">社交</span>
            <a target="_blank" rel="external nofollow noopener noreferrer" href="">GitHub</a>
            <a target="_blank" rel="external nofollow noopener noreferrer" href="">GitLab</a>
        </div>
        <div class="sitemap-group">
            <span class="fs14">更多</span>
            <a href="/about/">关于本站</a>
            <a href="/bibi/">哔哔</a>
            <a target="_blank" rel="external nofollow noopener noreferrer" href="https://image.jingzhe.xyz">图床</a>
        </div>
    </div>
    <div class="text">
        <p>本博客所有文章除特别声明外，均采用
            <a target="_blank" rel="external nofollow noopener noreferrer"
                href="https://creativecommons.org/licenses/by-nc-sa/4.0/">
                CCBY-NC-SA 4.0
            </a>
            许可协议，转载请注明出处。
        </p>
    </div>
</div>
    </div>
    <aside class="l_right">
        
<header class="header">
    <div class="logo-wrap">
        <a class="avatar" href="/about/">
            <img class="avatar" src="" data-original="https://ae01.alicdn.com/kf/Ua26a9ebfe30847ff8d4d7d7c7647c61cH.jpg"></a>
        <a class="title" href="/">
            <div class="main">惊蛰</div>
            <div class="sub normal cap"></div>
            <div class="sub hover cap" style="opacity:0">jingzhe.xyz</div>
        </a>
    </div>
    <nav class="menu dis-select">
        <a class="nav-item " href="/">博客</a>
        <a class="nav-item " href="/archives/">归档</a>
        <a class="nav-item " href="/about/">关于</a>
    </nav>
</header>
<script>
    $(function () {
        // https://v1.hitokoto.cn/?c=f&encode=text&max_length=8
        $.ajax({
            url: "https://v1.hitokoto.cn/?c=f&encode=text&max_length=10&min_length=5&c=i",
            success: function (result) {
                $(".normal").html(result);
            }
        });

    })
</script>
<div class="widgets">
    <div class="widget-wrap">
    <div class="widget-header cap dis-select"><span class="name">目录</span></div>
    <div class="widget-body fs14 toc-body">
        <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#BIO%EF%BC%8CNIO%E5%92%8CAIO"><span class="toc-text">BIO，NIO和AIO</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#BIO"><span class="toc-text">BIO</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#NIO"><span class="toc-text">NIO</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#AIO-NIO-2"><span class="toc-text">AIO(NIO.2)</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%A4%9A%E8%B7%AF%E5%A4%8D%E7%94%A8%EF%BC%88Channel%EF%BC%89"><span class="toc-text">多路复用（Channel）</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%8B%86%E5%8C%85-%E7%B2%98%E5%8C%85"><span class="toc-text">拆包&#x2F;粘包</span></a></li></ol></li></ol>
    </div>

    <!-- 
     -->
</div>

<script>
    // 让锚点位置下降80个点数
    $(".body-main-content *[id]").each(i => {
        $($(".body-main-content *[id]")[i]).attr('style', 'padding-top: 60px;margin-top: -60px;');
    })

    $(function () {
        $('.toc-link').click(function () {
            if (location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') && location
                .hostname == this.hostname) {
                var $target = $(this);
                $target = $('[id=' + decodeURI(this.hash.slice(1)) + ']');
                if ($target.length) {
                    var targetOffset = $target.offset().top;
                    $('html,body').animate({
                            scrollTop: targetOffset
                        },
                        700);
                    return false;
                }
            }
        });
    })
</script>
    <footer class="footer dis-select">
    <div class="social-wrap">
        <a class="social" title="GitHub" href="https://github.com/xaoxuu" target="_blank"
            rel="external nofollow noopener noreferrer"><img class="lazy entered loaded"
                src="https://cdn.jsdelivr.net/gh/cdn-x/placeholder@1.0.3/social/08a41b181ce68.svg"
                data-src="https://cdn.jsdelivr.net/gh/cdn-x/placeholder@1.0.3/social/08a41b181ce68.svg"
                data-ll-status="loaded"></a><a class="social" title="Spotify"
            href="https://open.spotify.com/user/xaoxuu" target="_blank"
            rel="external nofollow noopener noreferrer"><img class="lazy entered loaded"
                src="https://cdn.jsdelivr.net/gh/cdn-x/placeholder@1.0.3/social/3845874.svg"
                data-src="https://cdn.jsdelivr.net/gh/cdn-x/placeholder@1.0.3/social/3845874.svg"
                data-ll-status="loaded"></a><a class="social" title="Unsplash"
            href="https://unsplash.com/@xaoxuu" target="_blank"
            rel="external nofollow noopener noreferrer"><img class="lazy entered loaded"
                src="https://cdn.jsdelivr.net/gh/cdn-x/placeholder@1.0.3/social/3616429.svg"
                data-src="https://cdn.jsdelivr.net/gh/cdn-x/placeholder@1.0.3/social/3616429.svg"
                data-ll-status="loaded"></a><a class="social" title="Comments" href="/about/#comments"
            rel="noopener noreferrer"><img class="lazy entered loaded"
                src="https://cdn.jsdelivr.net/gh/cdn-x/placeholder@1.0.3/social/942ebbf1a4b91.svg"
                data-src="https://cdn.jsdelivr.net/gh/cdn-x/placeholder@1.0.3/social/942ebbf1a4b91.svg"
                data-ll-status="loaded"></a></div>
</footer>
</div>
    </aside>
</div>

<style>

</style>
    <div class="loading-div" id="loading" style="display: none;">
    <div class="loading">
        <svg id="stage" viewBox="0 0 500 500" preserveAspectRatio="xMidYMid meet" stroke="#fff" stroke-width="3"
            stroke-linecap="round" fill="none"></svg>
        <span>加载中...</span>
    </div>
</div>
<style>
    .loading-div {
        position: absolute;
        display: flex;
        top: 0px;
        width: 100%;
        height: 100%;
        align-items: center;
        z-index: 20;
        animation-name: fadeIn;
        animation-duration: 2s;
        animation-fill-mode: forwards;
    }

    /*动画效果*/
    @keyframes fadeIn {
        from {
            backdrop-filter: blur(0px);
            background: rgba(0, 0, 0, 0);
        }

        to {
            backdrop-filter: blur(2px);
            background: rgb(0, 0, 0, 0.5);
        }
    }

    /*动画效果*/
    @keyframes fadeIn1 {
        from {
            filter: blur(1px);
        }

        to {
            filter: blur(0px);
        }
    }

    .loading {
        color: #fff;
        width: 250px;
        height: 250px;
        margin: 0 auto;
        display: flex;
        align-items: center;
        justify-content: center;
        align-content: center;
        flex-wrap: nowrap;
        flex-direction: column;
        animation-name: fadeIn1;
        animation-duration: 2s;
        animation-fill-mode: forwards;
    }

    svg {
        width: 100%;
        height: 100%;
        margin: 0;
        padding: 0;
        /* background: #000; */
    }
</style>
<script src="https://lib.baomitu.com/gsap/3.7.1/gsap.min.js"></script>
<script>
    let pts = [],
        nPts = 12,
        lineLength = 60,
        timeScale = 0.5

    const radius = 180,
        tl = gsap.timeline()

    for (let i = 0; i < nPts; i++) { // plot points + add circles
        const c = document.createElementNS("http://www.w3.org/2000/svg", "circle"),
            angle = (i / nPts * Math.PI * 2) - Math.PI / 2,
            x = Math.cos(angle) * radius,
            y = Math.sin(angle) * radius

        pts.push(x.toFixed(2) + ',' + y.toFixed(2) + ' ')

        gsap.set(c, {
            x: 250,
            y: 250,
            scale: 0.8,
            attr: {
                class: 'c' + i,
                r: 3,
                cx: x,
                cy: y,
                fill: '#fff',
                stroke: 'none'
            }
        })

        stage.appendChild(c);
    }


    for (let i = 0; i <= nPts; i++) { // add paths + animate
        const p = document.createElementNS("http://www.w3.org/2000/svg", "path")

        gsap.set(p, {
            x: 250,
            y: 250,
            attr: {
                class: 'line' + i,
                d: 'M' + pts[i] + ' C' + gsap.utils.wrap(pts, [i + 2]) + ' ' + gsap.utils.wrap(pts, [i - 5]) +
                    ' ' + gsap.utils.wrap(pts, [i - 2]),
                // d:'M'+pts[i]+' c 0,0 '+gsap.utils.wrap(pts,[i-nPts/2])+' '+gsap.utils.wrap(pts,[i-3]),
                'stroke-dasharray': lineLength + ' ' + lineLength,
                'stroke-dashoffset': lineLength
            },
        });

        stage.appendChild(p);

        const lineTL = gsap.timeline({
                repeat: -1,
                defaults: {
                    duration: 0.4
                }
            })
            .to(p, {
                attr: {
                    'stroke-dashoffset': -lineLength
                },
                ease: 'expo.inOut',
                duration: 0.6
            }, 0.4)

        if (i < nPts) {
            lineTL.to('.c' + i, {
                scale: 1.2,
                ease: 'expo.inOut'
            }, 0.1)
            lineTL.to('.c' + i, {
                scale: 0.8,
                ease: 'expo.in'
            }, 0.5)
        }

        tl.add(lineTL, 1 - i / nPts);
    }

    tl.play(5).timeScale(timeScale)
</script>
<script>
    $(function () {
        // loadingShow()
    })

    function loadingShow() {
        $("#loading").show();
    }

    function loadingHide() {
        $("#loading").hide();
    }
</script>

    <!-- <script src="https://cdn.bootcdn.net/ajax/libs/jquery.scrollbar/0.2.9/jquery.scrollbar.min.js"></script> -->
    <script>
        // $(document).ready(function () {
        //     $('.l_body').scrollbar({
        //         barWidth: 10, //滚动条的宽度(这里根据需要写数值即可，不设置是10,即默认10px)
        //         position: "x,y", //写“x”代表只出水平滚动条，写“y”表示只出垂直滚动条，写“x,y”则出水平和垂直滚动条（只有在内容超出容器时才出现滚动条）
        //         wheelDis: 15 //滚轮滚动一次向下或向上滚动的距离，默认是15，可根据需要修改数值
        //     });
        // });
    </script>
    <script>
        $('img').lazyload({
            threshold: 0,
            failure_limit: 0,
            event: "scroll", //触发事件
            effect: "show", //显示方式
            container: window, //容器
            data_attribute: "original", //属性
            skip_invisible: true,
            appear: null,
            load: null,
            placeholder: ""
        });
    </script>
<script>
            window.imageLazyLoadSetting = {
                isSPA: false,
                preloadRatio: 3,
                processImages: null,
            };
        </script><script>window.addEventListener("load",function(){var t=/\.(gif|jpg|jpeg|tiff|png)$/i,r=/^data:image\/[a-z]+;base64,/;Array.prototype.slice.call(document.querySelectorAll("img[data-original]")).forEach(function(a){var e=a.parentNode;"A"===e.tagName&&(e.href.match(t)||e.href.match(r))&&(e.href=a.dataset.original)})});</script><script>!function(n){n.imageLazyLoadSetting.processImages=o;var e=n.imageLazyLoadSetting.isSPA,i=n.imageLazyLoadSetting.preloadRatio||1,r=Array.prototype.slice.call(document.querySelectorAll("img[data-original]"));function o(){e&&(r=Array.prototype.slice.call(document.querySelectorAll("img[data-original]")));for(var t,a=0;a<r.length;a++)0<=(t=(t=r[a]).getBoundingClientRect()).bottom&&0<=t.left&&t.top<=(n.innerHeight*i||document.documentElement.clientHeight*i)&&function(){var t,e,n,i,o=r[a];t=o,e=function(){r=r.filter(function(t){return o!==t})},n=new Image,i=t.getAttribute("data-original"),n.onload=function(){t.src=i,e&&e()},t.src!==i&&(n.src=i)}()}o(),n.addEventListener("scroll",function(){var t,e;t=o,e=n,clearTimeout(t.tId),t.tId=setTimeout(function(){t.call(e)},500)})}(this);</script></body>